#ifndef _IO_TCP_H_
#define _IO_TCP_H_

#include <ioeventdef.h>

#define TCP_CONNECTED		0
#define TCP_CONNECT_FAILD	1
#define TCP_CONNECT_TIMEOUT	2

class IoTcpBase
{
public:
	IoTcpBase() {
		this->ptr = NULL;
		this->safe = NULL;
		this->tm = NULL;
		conn = 0;
		s_memset(&this->ctx, 0, sizeof(io_data_t));
	}
	virtual ~IoTcpBase() {
		tcp_destory();
	}
public:
	int tcp_create(io_service* pService) {

		this->ptr = ALLOC_NEW io_socket(*pService);
		rc_assert(this->ptr, S_ERROR)

		this->tm = ALLOC_NEW io_time(*pService);
		rc_assert(this->tm, S_ERROR)

		this->safe = ALLOC_NEW io_strand(*pService);
		rc_assert(this->safe, S_ERROR)

		return S_SUCCESS;
	}
	int tcp_destory() {

		rc_assert(this->ptr, S_ERROR)
		delete this->ptr;
		this->ptr = NULL;

		rc_assert(this->tm, S_ERROR)
		delete this->tm;
		this->tm = NULL;

		rc_assert(this->safe, S_ERROR)
		delete this->safe;
		this->safe = NULL;

		return S_SUCCESS;
	}
	int tcp_assign(_sock_t* s) {

		rc_assert(this->ptr, S_ERROR)
		io_error_code ec;
		this->ptr->assign(boost::asio::ip::tcp::v4(), *s, ec);

		rc_assert(!ec, S_ERROR)
		rc_assert(this->ptr->is_open(), S_ERROR)

		return S_SUCCESS;
	}

	int	tcp_close() {

		rc_assert(this->ptr, S_ERROR)
		rc_assert(this->tm, S_ERROR)
		rc_assert(this->ptr->is_open(), S_ERROR)

		int rc = S_SUCCESS;
		io_error_code ec;
		try {

			this->tm->cancel(ec);
			if (ec)
			{
				loge("this->tm->cancel==>%s", ec.message().c_str());
			}

			this->ptr->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
			if (ec)
			{
				loge("this->ptr->shutdown==>%s", ec.message().c_str());
			}

			//this->ptr->cancel(); //win xp ==> No support

			this->ptr->close(ec);
			if (ec)
			{
				loge("this->ptr->close:%s", ec.message().c_str());
			}

		} catch (std::exception err) {
			loge("std::exception:%s", err.what());
			rc = S_ERROR;
		}

		return rc;
	}
	//https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/example/cpp03/timeouts/async_tcp_client.cpp
	int	tcp_async_connect(void* data, const char* addr, unsigned short port, int timeout) {

		rc_assert(this->ptr, S_ERROR)
		rc_assert(this->tm, S_ERROR)

		int rc = S_SUCCESS;

		try {
			this->ptr->async_connect(
				tcp::endpoint(boost::asio::ip::address::from_string(addr), port),
				boost::bind(&IoTcpBase::tcp_connect_handler,
				this,
				boost::asio::placeholders::error));

			this->tm->expires_from_now(boost::posix_time::seconds(timeout));
			this->tm->async_wait(boost::bind(&IoTcpBase::tcp_connect_timeout_handler,
				this,
				boost::asio::placeholders::error));

		} catch (stdexcep err) {
			loge("std::exception:%s", err.what());
			rc = S_ERROR;
		}
		return rc;
	}

	int	tcp_sync_connect(const char* addr, unsigned short port) {

		//
		//http://stackoverflow.com/questions/1511129/boostasioiptcpsocket-is-connected
		//
		rc_assert(this->ptr, S_ERROR)

		int rc = S_SUCCESS;

		try {
			this->ptr->connect(tcp::endpoint(boost::asio::ip::address::from_string(addr), port));
		} catch (stdexcep err) {
			loge("std::exception:%s", err.what());
			rc = S_ERROR;
		}
		return rc;
	}
	int tcp_is_open() {

		rc_assert(this->ptr->is_open(), S_ERROR)
		return S_SUCCESS;
	}
	int tcp_setoption() {

		rc_assert(this->ptr, S_ERROR)

		int rc = S_SUCCESS;

		try {
			this->ptr->set_option(boost::asio::ip::tcp::no_delay(true));
			this->ptr->set_option(boost::asio::socket_base::linger(true, 5));

		} catch (stdexcep err) {
			loge("std::exception:%s", err.what());
			rc = S_ERROR;
		}
		return rc;
	}

	int tcp_connect_handler(io_c_error_code ec) {

		if (ec) {
			if (ec.value() != boost::system::errc::operation_canceled) {

			}
			this->conn = TCP_CONNECT_FAILD;
			this->ptr->close();
			this->ConnectCB(this->ctx.data, TCP_CONNECT_FAILD);

		} else {
			this->tcp_setoption();
			this->conn = TCP_CONNECTED;
			this->ConnectCB(this->ctx.data, TCP_CONNECTED);
		}

		this->tm->cancel();
		return S_SUCCESS;
	}
	int tcp_connect_timeout_handler(io_c_error_code ec) {

		if (ec) {
			if (ec.value() != boost::system::errc::operation_canceled) {
				this->ConnectCB(this->ctx.data, TCP_CONNECT_FAILD);
				this->conn = TCP_CONNECT_FAILD;
			}
		} else {
			conn = TCP_CONNECT_TIMEOUT;
			this->ConnectCB(this->ctx.data, TCP_CONNECT_TIMEOUT);
		}
		return S_SUCCESS;
	}

public:
	io_socket_ptr		ptr;
	io_strand_ptr		safe;
	io_time_ptr			tm;
	io_data_t			ctx;
	int					conn;
public:
	virtual void ConnectCB(void *data, event_id error_code) = 0;

};

#endif 
